package uebung04_h;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;


public class SurfTester {
	/**
	 * This function evaluates a rectangular response from an integral image
	 * It functions like that:
	 * 0 1
	 * 2 3
	 * response(0,3) = 3-1-2+0
	 * @param intImg integral image data
	 * @param x0 x coordinate of the upper left corner
	 * @param y0 y coordinate of the upper left corner
	 * @param x1 x coordinate of the lower right corner
	 * @param y1 y coordinate of the lower right corner
	 * @param w	width of the integral image
	 * @param h height of the integral image
	 * @return
	 */
	public static double getRect(double[] intImg, int x0, int y0, int x1, int y1,int w, int h) {
		// need regularized geometry, where does the ridge belong to?
		int index0 = (x0-1)+(y0-1)*w, index3 = x1+y1*w, index2 = (x0-1)+y1*w, index1 = x1+(y0-1)*w;
		return intImg[index3]-(((y0-1)<0)?0:intImg[index1])-(((x0-1)<0)?0:intImg[index2])+(((x0-1)<0||(y0-1)<0)?0:intImg[index0]);
	}
	
	public static double evaluateDxx(double[] intImg, int filtersize, int x, int y, int w, int h) {
		int whalf = (filtersize - 1) / 2, rectX = filtersize / 3, rectY = (5 + (4 * (filtersize - 9)) / 6)/2;
		int x1 = x - whalf, x4 = x + whalf, x2 = x1 + rectX, x3 = x4 - rectX, y1 = y - rectY, y2 = y + rectY;
		return getRect(intImg, x1, y1, x4, y2, w, h)- 3*getRect(intImg, x2, y1, x3, y2, w, h);
	}
	public static double evaluateDxy(double[] intImg, int filtersize, int x, int y, int w, int h) {
		int rect = filtersize / 3;
		int x0 = x-1-rect, x1 = x-1, x2 = x + 1, x3 = x + 1 + rect, y0 = y-1-rect, y1 = y-1, y2 = y+1, y3 = y+1+rect;
		return getRect(intImg, x0, y0, x1, y1, w, h)+getRect(intImg, x2, y2, x3, y3, w, h)-getRect(intImg, x0, y2, x1, y3, w, h)-getRect(intImg, x2, y0, x3, y1, w, h);
	}
	public static double evaluateDyy(double[] intImg, int filtersize, int x, int y, int w, int h) {
		int whalf = (filtersize - 1) / 2, rectY = filtersize / 3, rectX = (5 + (4 * (filtersize - 9)) / 6)/2;
		int y1 = y - whalf, y4 = y + whalf, y2 = y1 + rectY, y3 = y4 - rectY, x1 = x - rectX, x2 = x + rectX;
		return getRect(intImg, x1, y1, x2, y4, w, h)- 3*getRect(intImg, x1, y2, x2, y3, w, h);
	}
	public static double evaluateDx(double[] intImg, int filtersize, int x, int y, int w, int h) {
		int f2 = filtersize/2;
		int x0 = x-f2, x1 = x, x2 = x, x3 = x+f2, y0 = y-f2, y1 = y+f2;
		return -getRect(intImg, x0, y0, x1, y1, w, h)+getRect(intImg, x2, y0, x3, y1, w, h);
	}
	public static double evaluateDy(double[] intImg, int filtersize, int x, int y, int w, int h) {
		int f2 = filtersize/2;
		int y0 = y-f2, y1 = y, y2 = y, y3 = y+f2, x0 = x-f2, x1 = x+f2;
		return -getRect(intImg, x0, y0, x1, y1, w, h)+getRect(intImg, x0, y2, x1, y3, w, h);
	}

	public static void main(String[] args) throws Exception {

		BufferedImage bi = ImageIO.read(SurfTester.class.getClassLoader().getResourceAsStream("res/test2.png"));
		
		final int w = bi.getWidth(), h = bi.getHeight();
		
		int[] pixels= bi.getRGB(0, 0, w, h, new int[w*h], 0, w);
		
		final double scale = 1./255.;
		
		double[] lum = new double[w*h];
		
		for (int i = 0; i<pixels.length; i++) {
			int rgb = pixels[i];
			double r = ((rgb>>16)&0xFF), g = ((rgb>>8)&0xFF), b = (rgb&0xFF);
			lum[i] = (0.299*r+0.587*g+0.114*b)*scale;
		}
		
		//calculating integral image
		double[] integral = new double[w*h];
		
		integral[0] = lum[0];
		
		for (int x = 1; x<w; x++) {
			integral[x] = integral[x-1]+lum[x];
		}
		for (int y = 1; y<h; y++) {
			double sum = 0;
			for (int x = 0; x<w; x++) {
				int indexnow = x+(y*w), indexformer = x+(y-1)*w;
				sum += lum[indexnow];
				integral[indexnow] = integral[indexformer]+sum;
			}
		}
		
		final double we = 0.912;
		
		//building Hessian matrix determinants
		double[] det = new double[w*h];
		
		int filterlength = 9, f2 = filterlength/2, filterSize = filterlength*filterlength;
		double filternorm = 1./(double)filterSize;
		for (int y = f2, endy = h-f2; y<endy; y++) {
			for (int x = f2, endx = w-f2; x<endx; x++) {
				int index = x+y*w;
				//furthermore, the filter responses are normalised with respect to their size ;)
				double dxx = evaluateDxx(integral, filterlength, x, y, w, h)*filternorm, dxy = evaluateDxy(integral, filterlength, x, y, w, h)*filternorm, dyy = evaluateDyy(integral, filterlength, x, y, w, h)*filternorm;
				det[index] = dxx*dyy-(we*dxy)*(we*dxy);
			}
		}
		
		int[] detshow = prepare(det, new int[w*h]);
		
		final BufferedImage show = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
		
		pixels = fuseRGB(detshow, detshow, detshow, pixels);
		show.setRGB(0, 0, w, h, pixels, 0, w);
		
		JFrame ff = new JFrame();
		ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		ff.add(new JComponent() {
			{setPreferredSize(new Dimension(w,h));}
			@Override
			public void paint(Graphics arg0) {
				arg0.drawImage(show, 0, 0, this);
				super.paint(arg0);
			}
		});
		
		ff.pack();
		ff.setVisible(true);
	}
	public static int[] prepare(double[] input, int[] fillme) {
		
		double max = Double.NEGATIVE_INFINITY, min = Double.POSITIVE_INFINITY;
		
		for (int i = 0; i<input.length; i++) {
			max = Math.max(input[i], max);
			min = Math.min(input[i], min);
		}
		double span = max-min;
		for (int i = 0; i<input.length; i++) {
			fillme[i] = (int)(255.*((input[i]-min)/span));
		}
		return fillme;
	}
	public static int[] fuseRGB(int[] r, int[] g, int[] b, int[] fillme) {
		for (int i = 0; i<fillme.length; i++) {
			fillme[i] = r[i]<<16 | g[i]<<8 | b[i] | 0xFF000000;
		}
		return fillme;
	}
}
